#pragma once
#include <type_traits>
#include <cstddef>
#include <functional>
// #include <stdexcept>

namespace net_tools::base{


template <typename Fun_Body,size_t Buffer_size = 64>
class Function;

template <typename Ret,typename... Args, size_t Buffer_size>
class Function<Ret(Args...), Buffer_size>{
  using Func = Ret(*)(Args...);
  using Call = Ret(*)(void*,Func,Args...);
  using Jobc = void(*)(void*,void*,char);
 public:
  Function()
    :func_body_(nullptr),
     call_ptr_(nullptr),
     jobc_ptr_(nullptr)
  {
  };
  Function(std::nullptr_t)
    :func_body_(nullptr),
     call_ptr_(nullptr),
     jobc_ptr_(nullptr)
  {
  };
  Function& operator=(std::nullptr_t){
    func_body_ = nullptr;
    call_ptr_ = nullptr;
    jobc_ptr_ = nullptr;
    return *this;
  };
  operator bool(){
    return call_ptr_;
  };

  // for funciton_ptr
  Function(Ret (*func_ptr)(Args...))
    :Function()
  {
    func_body_ = func_ptr;
    call_ptr_ = [](void*, Func f_ptr, Args... args) -> Ret
    {
      return static_cast<Ret(*)(Args...)>(f_ptr)(args...);
    };
  };
  Function& operator=(Ret (*func_ptr)(Args...))
  {
    func_body_ = func_ptr;
    call_ptr_ = [](void*, Func f_ptr, Args... args) -> Ret
    {
      return static_cast<Ret(*)(Args...)>(f_ptr)(args...);
    };
    jobc_ptr_ = nullptr;
    return *this;
  };
  // end for function_ptr

  // 同类型赋值拷贝构造
  Function& operator=(const Function& fun)
  {
    if(this == &fun){
      return *this;
    };
    Copy_from_same(fun);
    return *this;
  };
    // 同类型赋值拷贝构造
  Function& operator=(Function& fun)
  {
    if(this == &fun){
      return *this;
    };
    Copy_from_same(fun);
    return *this;
  };
  // 同类型移动赋值构造
  Function& operator=(Function&& fun)
  {
    if(this == &fun){
      return *this;
    };
    Move_from_same(fun);
    return *this;
  };
  // 同类型拷贝构造
  Function(Function& fun)
    :Function()
  {
    Copy_from_same(fun);
  };
  // 同类型拷贝构造
  Function(const Function& fun)
    :Function()
  {
    Copy_from_same(fun);
  };
  // 同类型移动构造
  Function(Function&& fun)
    :Function()
  {
    Move_from_same(fun);
  };


  // For std::function lambda
  template <typename Func_body>
  Function(Func_body&& fun)
    :Function()
  {
    using Type = std::decay_t<Func_body>;
    static_assert(sizeof(Type) <= Buffer_size - 2 * sizeof(void*), "function_body too big");

    call_ptr_ = [](void* object_ptr,Func,Args... args) -> Ret
    {
      return static_cast<Type*>(object_ptr)->operator()(args...);
    };
    jobc_ptr_ = [](void* buffer_ptr,void* object_ptr,char job_type)
    {
      switch (job_type)
      {
      case 0:
        new(buffer_ptr) Type(*(static_cast<Type*>(object_ptr)));
        break;
      case 1:
        new(buffer_ptr) Type(std::move(*(static_cast<Type*>(object_ptr))));
        break;
      case 2:
        reinterpret_cast<Type*>(buffer_ptr)->~Type();
        break;
      }
    };
    jobc_ptr_(&buffer_,&fun,1);
  };
  // For std::function lambda
  template <typename Func_body>
  Function(const Func_body& fun)
    :Function()
  {
    using Type = std::decay_t<Func_body>;
    static_assert(sizeof(Type) <= Buffer_size - 2 * sizeof(void*), "function_body too big");

    call_ptr_ = [](void* object_ptr,Func,Args... args) -> Ret
    {
      return static_cast<Type*>(object_ptr)->operator()(args...);
    };
    jobc_ptr_ = [](void* buffer_ptr,void* object_ptr,char job_type)
    {
      switch (job_type)
      {
      case 0:
        new(buffer_ptr) Type(*(static_cast<Type*>(object_ptr)));
        break;
      case 1:
        new(buffer_ptr) Type(std::move(*(static_cast<Type*>(object_ptr))));
        break;
      case 2:
        reinterpret_cast<Type*>(buffer_ptr)->~Type();
        break;
      }
    };
    jobc_ptr_(&buffer_,&fun,0);
  };
  // For std::function lambda
  template <typename Func_body>
  Function& operator=(Func_body&& fun)
  {
    using Type = std::decay_t<Func_body>;
    static_assert(sizeof(Type) <= Buffer_size - 2 * sizeof(void*), "function_body too big");
    if(reinterpret_cast<void*>(this) == reinterpret_cast<void*>(&fun)){
      return *this;
    };
    call_ptr_ = [](void* object_ptr,Func,Args... args) -> Ret
    {
      return static_cast<Type*>(object_ptr)->operator()(args...);
    };
    jobc_ptr_ = [](void* buffer_ptr,void* object_ptr,char job_type)
    {
      switch (job_type)
      {
      case 0:
        new(buffer_ptr) Type(*(static_cast<Type*>(object_ptr)));
        break;
      case 1:
        new(buffer_ptr) Type(std::move(*(static_cast<Type*>(object_ptr))));
        break;
      case 2:
        reinterpret_cast<Type*>(buffer_ptr)->~Type();
        break;
      }
    };
    jobc_ptr_(&buffer_,&fun,1);
    return *this;
  };
  // For std::function lambda
  template <typename Func_body>
  Function& operator=(const Func_body& fun)
  {
    using Type = std::decay_t<Func_body>;
    static_assert(sizeof(Type) <= Buffer_size - 2 * sizeof(void*), "function_body too big");
    if(reinterpret_cast<void*>(this) == reinterpret_cast<void*>(&fun)){
      return *this;
    };
    call_ptr_ = [](void* object_ptr,Func,Args... args) -> Ret
    {
      return static_cast<Type*>(object_ptr)->operator()(args...);
    };
    jobc_ptr_ = [](void* buffer_ptr,void* object_ptr,char job_type)
    {
      switch (job_type)
      {
      case 0:
        new(buffer_ptr) Type(*(static_cast<Type*>(object_ptr)));
        break;
      case 1:
        new(buffer_ptr) Type(std::move(*(static_cast<Type*>(object_ptr))));
        break;
      case 2:
        reinterpret_cast<Type*>(buffer_ptr)->~Type();
        break;
      }
    };
    jobc_ptr_(&buffer_,&fun,0);
    return *this;
  };

  Ret operator()(Args... args){
    // if(!call_ptr_) throw std::runtime_error("bad function");
    // 如果没有加fno-exceptions 则开启
    return call_ptr_(&buffer_,func_body_,args...);
  }
  ~Function(){
    if(jobc_ptr_){
      jobc_ptr_(&buffer_,nullptr,2);
    }
  };
 private:
  void Copy_from_same(const Function& fun){
    if(jobc_ptr_){
      jobc_ptr_(&buffer_,nullptr,2);
    }
    jobc_ptr_ = fun.jobc_ptr_;
    call_ptr_ = fun.call_ptr_;
    func_body_ = fun.func_body_;
    if(jobc_ptr_){
      jobc_ptr_(&buffer_,const_cast<void*>(reinterpret_cast<const void*>(&fun.buffer_)),0);
    }
  };
  void Move_from_same(Function& fun){
    if(jobc_ptr_){
      jobc_ptr_(&buffer_,nullptr,2);
    }
    jobc_ptr_ = fun.jobc_ptr_;
    fun.jobc_ptr_ = nullptr;
    call_ptr_ = fun.call_ptr_;
    fun.call_ptr_ = nullptr;
    func_body_ = fun.func_body_;
    if(jobc_ptr_){
      jobc_ptr_(&buffer_,&fun.buffer_,1);
    }
    fun.func_body_ = nullptr;
  };
	union{
		typename std::aligned_storage<Buffer_size - 2 * sizeof(void*), sizeof(size_t)>::type buffer_;
		Func func_body_;
	};
	Call call_ptr_;
	Jobc jobc_ptr_;
};

} // namespace net_tools::base